package ro.prjmanager.core.list;

import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.impl.LogFactoryImpl;
import org.springframework.beans.BeanWrapperImpl;
import ro.prjmanager.core.annotations.Column;
import ro.prjmanager.core.annotations.EditOptions;
import ro.prjmanager.core.annotations.SelectOption;
import ro.prjmanager.core.context.PrjManagerWebContextBuilder;
import ro.prjmanager.core.dao.Filter;
import ro.prjmanager.core.service.Join;
import ro.prjmanager.core.service.Order;
import ro.prjmanager.core.service.SQLQuery;
import ro.prjmanager.core.service.Service;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;


@SuppressWarnings({"ALL"})
public class ListServlet extends GenericServlet {
    public static final Log log = LogFactoryImpl.getLog(ListServlet.class);
    Class clazz;
    ListUtil listUtil;

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/plain");
        ServletOutputStream out = response.getOutputStream();
        out.println(loadData(request));
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/plain");
        ServletOutputStream out = response.getOutputStream();
        out.println(loadData(request));
    }

    private void populateActionBeanFromRequest(Object objClazz, HttpServletRequest request) throws InvocationTargetException, IllegalAccessException {
        BeanUtilsBean bn = BeanUtilsBean.getInstance();
        Map params = request.getParameterMap();
        Set<String> keySet = params.keySet();
        if (keySet != null)
            for (String key : keySet) {
                Field[] fields = clazz.getDeclaredFields();
                if (fields != null)
                    for (Field field : fields) {
                        if (field.getName().equals(key)) {
                            bn.setProperty(objClazz, key, request.getParameter(key));
                            break;
                        }
                    }
            }
    }

    private void populateActionBeanFromSession(Object objClazz, HttpServletRequest request) throws InvocationTargetException, IllegalAccessException {
        BeanUtilsBean bn = BeanUtilsBean.getInstance();
        Object objSess = request.getSession().getAttribute(ListSessionAttributes.CUMULATE_FILTERS_KEY.getSessionKey());
        if (objSess != null) {
            Map<String, FilterPair> mapSess = (Map<String, FilterPair>) objSess;
            Set<String> sessKeys = mapSess.keySet();
            for (String key : sessKeys) {
                Field[] fields = clazz.getDeclaredFields();
                if (fields != null)
                    for (Field field : fields) {
                        if (field.getName().equals(mapSess.get(key).getPropertyName())) {
                            bn.setProperty(objClazz, key, mapSess.get(key).getPropertyValue());
                            break;
                        }
                    }
            }
        }
    }

    private SQLQuery invokeActionBeanMethod(RequestParamList rpl, HttpServletRequest request) {
        SQLQuery sqlQuery = getSql(rpl);
        if (rpl.getMethName() != null) {
            try {
                Method meth = clazz.getMethod(rpl.getMethName(), SQLQuery.class, HttpServletRequest.class);
                Object objClazz = clazz.newInstance();

                populateActionBeanFromRequest(objClazz, request);
                populateActionBeanFromSession(objClazz, request);

                sqlQuery = (SQLQuery) meth.invoke(objClazz, sqlQuery, request);
            } catch (NoSuchMethodException e) {
                log.warn("No method founded!!!");
            } catch (InvocationTargetException e) {
                log.warn(e);
            } catch (InstantiationException e) {
                log.error(e);
            } catch (IllegalAccessException e) {
                log.error(e);
            }
        }
        return sqlQuery;
    }

    private String toJqGrid(Object actionBeanInstance, java.util.List lst, RequestParamList rpl, int nos) {
        Field[] fields = actionBeanInstance.getClass().getDeclaredFields();
        int num = 0;
        if (fields != null && fields.length > 0) {

            for (Field field : fields) {
                Column col = field.getAnnotation(ro.prjmanager.core.annotations.Column.class);
                if (col != null)
                    num++;
            }
        }
        TabelOutJson tabelOutJson = new TabelOutJson(rpl.getRows(), nos, rpl.getPage(), lst != null ? lst.size() : 0, nos);
        BeanWrapperImpl bean = new BeanWrapperImpl();
        int pag = 0;
        if (lst != null && !lst.isEmpty())
            for (Object row : lst) {
                if (fields != null && fields.length > 0) {
                    int cell = 0;
                    RowJson rowJson = new RowJson(num);
                    for (Field field : fields) {
                        Column col = field.getAnnotation(ro.prjmanager.core.annotations.Column.class);
                        if (col != null) {
                            String attName = col.index();
                            bean.setWrappedInstance(row);
                            Object objAttVal = bean.getPropertyValue(attName);
                            SelectOption selectOption = field.getAnnotation(SelectOption.class);
                            if (selectOption != null) {
                                if (objAttVal != null) {
                                    bean.setWrappedInstance(objAttVal);
                                    String attNameNew = selectOption.nameOption();
                                    Object objAttValId = bean.getPropertyValue(attNameNew);
                                    rowJson.setCellByNo(cell++, objAttValId != null ? objAttValId.toString() : "");
                                } else {
                                    rowJson.setCellByNo(cell++, "");
                                }
                            } else {
                                EditOptions editOptions = col.editOptions();
                                if (editOptions.edittype().equals("file")) {
                                    String ss = "<a href=\"#\" onclick=\"downloadFile(" + getId(row).toString() + ",\\'" + col.index() + "\\',\\'" + listUtil.getServiceName() + "\\');\">Click to see file</a>";
                                    rowJson.setCellByNo(cell++, ss);
                                } else

                                    rowJson.setCellByNo(cell++, objAttVal != null ? objAttVal.toString() : "");
                            }
                        }
                    }
                    rowJson.setId(getId(row).toString());
                    tabelOutJson.setRowByNo(pag++, rowJson);
                }
            }


        return tabelOutJson.toJson();

    }

    private String loadData(HttpServletRequest request) {
        PrjManagerWebContextBuilder.getBuilder().set(request, null);
        TabelOutJson tabelOutJson = null;
        BeanWrapperImpl bean = new BeanWrapperImpl();
        RequestParamList rpl = new RequestParamList(request);
        try {
            clazz = Class.forName(rpl.getClassModel());
            listUtil = new ListUtil(clazz);
            listUtil.setup();
            Object obj = clazz.newInstance();
            Service service = getServiceList(request);
            SQLQuery sqlQuery = invokeActionBeanMethod(rpl, request);
            java.util.List lst = service.listPag(sqlQuery);
            int nos = service.no(sqlQuery);

            return toJqGrid(obj, lst, rpl, nos);
        }catch (Exception e){
            log.error(e);
            throw new RuntimeException(e);
        }
    }


    private String getSQLFoot(RequestParamList requestParamList) {
        StringBuilder ret = new StringBuilder();
        String orderStr = null;
        if (listUtil.findByName(requestParamList.getSidx()) != null && listUtil.findByName(requestParamList.getSidx()).isSelector()) {
            orderStr = requestParamList.getSidx();
            ret.append("left join c.").append(requestParamList.getSidx());
        }


        if (requestParamList.isSearch() && requestParamList.getSearchString() != null && requestParamList.getSearchString().length() > 0) {
            String sqlColSearch = listUtil.getStringCol(requestParamList.getSearchField());
            if (listUtil.findByName(requestParamList.getSearchField()).isSelector() && !requestParamList.getSearchField().equals(orderStr)) {
                ret.append("left join c.").append(requestParamList.getSearchField());

            }
            ret.append(" where ");
            ret.append(" c.").append(sqlColSearch).append("='").append(requestParamList.getSearchString()).append("'");

        }

        String orderCol = listUtil.getStringCol(requestParamList.getSidx());


        ret.append(" order by c.").append(orderCol).append(" ").append(requestParamList.getSord());


        return ret.toString();
    }

    private SQLQuery getSql(RequestParamList requestParamList) {
        SQLQuery ret = new SQLQuery();
        ret.setStartPage((requestParamList.getPage() - 1) * requestParamList.getRows());
        ret.setResPerPage(requestParamList.getRows());
        String orderStr = null;
        if (listUtil.findByName(requestParamList.getSidx()) != null && listUtil.findByName(requestParamList.getSidx()).isSelector()) {
            orderStr = requestParamList.getSidx();
            ret.addJoin(new Join("c." + orderStr, "left", ""));
        }

        if (requestParamList.isSearch() && requestParamList.getSearchString() != null && requestParamList.getSearchString().length() > 0) {
            String sqlColSearch = listUtil.getStringCol(requestParamList.getSearchField());
            if (listUtil.findByName(requestParamList.getSearchField()).isSelector() && !requestParamList.getSearchField().equals(orderStr)) {
                ret.addJoin(new Join("c," + requestParamList.getSearchField(), "left", ""));
            }
            ret.addFilter(new Filter(sqlColSearch, requestParamList.getSearchString(), "c", "="));
        }
        String orderCol = listUtil.getStringCol(requestParamList.getSidx());
        ret.addOrder(new Order(orderCol, "c", requestParamList.getSord().equals("asc")));

        return ret;
    }

    private Object getId(Object row) {
        Object ret = null;
        Method[] meths = row.getClass().getMethods();
        for (Method meth : meths) {
            if (meth.getAnnotation(javax.persistence.Id.class) != null) {
                try {
                    ret = meth.invoke(row);
                } catch (IllegalAccessException e) {
                    log.error(e);
                } catch (InvocationTargetException e) {
                    log.error(e);
                }
            }
        }
        return ret;
    }
}
